package com.wutianyi.images;

import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.*;
import javax.swing.border.*;

// This is an overly simple extension of <JButton> that has an
// elliptical shape rather than rectangular.  For demonstration
// purposes, this component was built with a 'private' UI, which
// is a quick-and-dirty way to get components to appear differently.
//
// Note: this is not an industrial-strength class and has many
// features omitted from the button's behavior, such as focus and
// default button handling, and the preferred size is not handled
// in an ideal way.  Also, icons on the left or right will look funny.
public class EllipseButton extends JButton
{
    // Constructor - only support this version for demo
    public EllipseButton(String text)
    {
        super(text);

        // Create the private UI for the component. The UI
        // is a delegate object that handles paint requests for
        // the component.
        this.setUI(EllipseButtonUI.createUI(this));

        // Get rid of the rectangular button border
        this.setBorder(null);

        // Set this so the background of the ellipse will not
        // be filled in with the background color
        this.setContentAreaFilled(false);

        // Add a nice chunky margin to give room for the ellipse
        this.setMargin(new Insets(8, 14, 8, 14));
    }

    // Test frame for our button
    public static void main(String[] args)
    {
        // Put a bigger button font in the UIManager
        UIManager.put("Button.font", new Font("Arial", 0, 20));

        // Create a frame and add the button
        JFrame frame = new JFrame();
        frame.setLocation(100, 100);
        frame.setSize(200, 80);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setTitle("Elliptical Button");

        // This is a trick to get a component centered in a container.
        // An undocumented side-effect of <GridBagLayout> will
        // center a component at its preferred size if it is added
        // without any constraints
        frame.getContentPane().setLayout(new GridBagLayout());
        EllipseButton button = new EllipseButton("Elliptical");
        frame.getContentPane().add(button);

        // Set the background to a tasteful color and show the frame
        frame.getContentPane().setBackground(new Color(230, 230, 150));
        frame.setVisible(true);
    }
}

// This UI delegate is extended from the basic button UI that is
// the base class for all button UI delegates in all PLAFs.
class EllipseButtonUI extends BasicButtonUI
{
    // Create a singleton instance to share among all instances
    // of <EllipseButton>.
    protected static EllipseButtonUI singleton = new EllipseButtonUI();

    // Create a static <Stroke> object for painting the
    // elliptical border.
    protected static Stroke thickStroke = new BasicStroke(2.0f);

    // Return the singleton instance for all component instances
    public static ComponentUI createUI(JComponent c)
    {
        return singleton;
    }

    // Paint the component - this version differs from the
    // one in <java.awt.Component> in that it has another
    // parameter specifying the particular component being
    // painted. This is what allows UI delegates (like this
    // one) to be shared among components.
    public void paint(Graphics g, JComponent c)
    {
        // Acquire an instance of <Graphics2D> and hint the
        // renderer to use anti-aliasing
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // Get the button's area from the component
        // reference.
        AbstractButton b = (AbstractButton) c;
        Rectangle viewRect = new Rectangle();
        viewRect.x = 0;
        viewRect.y = 0;
        viewRect.width = b.getWidth() - 1;
        viewRect.height = b.getHeight() - 1;

        // Shrink everything by a pixel so the extra pixels created
        // by anti-aliasing will fit
        viewRect.grow(-2, -2);

        // Create a Java2D ellipse shape that is bounded by
        // the button's visible rectangle
        Ellipse2D ellipse = new Ellipse2D.Float();
        ellipse.setFrame(viewRect.getX(), viewRect.getY(), viewRect.getWidth(), viewRect.getHeight());

        // Determine if the button is being pressed or not. For
        // momentary buttons, the button must be 'armed' and 'pressed'
        // to appear in the pressed position (see <ButtonModel>).
        ButtonModel model = b.getModel();
        boolean pressed = (model.isArmed() && model.isPressed()) || model.isSelected();
        pressed = !pressed;
        // If the button is pressed, set a dark background, otherwise
        // paint the background according to the platform color
        // scheme for controls. Notice the use of <setPaint()>
        // instead of <setColor()>, although if the parameter
        // is of type <Color> there is no difference.
        if (pressed)
        {
            // It is possible that this <UIDefaults> key is not
            // there because it is not defined in <BasicLookAndFeel>
            Color background = UIManager.getColor("Button.select");
            g2.setPaint(background == null ? Color.gray : background);
        }
        else
            g2.setPaint(UIManager.getColor("control"));

        // Fill the ellipse with the background color
        g2.fill(ellipse);

        // To create the elliptical button border, we will borrow
        // the existing elliptical shape and create an <Arc2D> that
        // has the same bounding rectangle.
        Arc2D arc = new Arc2D.Float();
        arc.setFrame(viewRect.getX(), viewRect.getY(), viewRect.getWidth(), viewRect.getHeight());
        arc.setArcType(Arc2D.OPEN);

        // To draw the highlights diagonally, use this method
        // to set the start and end of the arc to be the upper
        // right and lower left of the bounding rectangle.
        arc.setAngles(viewRect.getWidth(), 0, 0, viewRect.getHeight());

        // Set the stroke to draw a thick line
        g2.setStroke(thickStroke);

        // Paint the upper left side of the arc. If the button
        // is pressed, paint it with shadow, otherwise paint with
        // highlight
        g2.setPaint(pressed ? UIManager.getColor("controlDkShadow") : UIManager.getColor("controlHighlight"));
        g2.draw(arc);

        // Flip the angles so the arc represents the other
        // portion of the ellipse. Paint the lower right side of
        // the arc. If the button is pressed, paint it with
        // highlight, otherwise paint with shadow.
        arc.setAngles(0, viewRect.getHeight(), viewRect.getWidth(), 0);
        g2.setPaint(pressed ? UIManager.getColor("controlHighlight") : UIManager.getColor("controlShadow"));
        g2.draw(arc);

        // Call the superclass to paint the text in the proper position.
        // The implementation of <BasicButtonUI> does not handle
        // focus, offsetting or default indication.
        super.paint(g, c);

        // To be a good citizen, change the rendering hints
        // of the graphic context back to the default.
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
    }

    // Return the 'preferred' size of the component based on the
    // size of the text. Cheat a little by calling the
    // superclass and tacking on enough extra space to ensure
    // there is sufficient room for the elliptical border.
    public Dimension getPreferredSize(JComponent c)
    {
        // Call the superclass
        AbstractButton b = (AbstractButton) c;
        Dimension dim = super.getPreferredSize(c);

        // Increment the preferred height and width by the
        // size of the margin
        dim.height += (b.getMargin().top + b.getMargin().bottom);
        dim.width += (b.getMargin().left + b.getMargin().right);
        return dim;
    }
}